#ifndef WINDOW_CLASS
#define WINDOW_CLASS

#include <windows.h>
#include <map>
#include <set>
#include <string>

#define DEF_HEIGHT        480
#define DEF_WIDTH         640
#define DEF_POS_X         0
#define DEF_POS_Y         0
#define DEF_CLASS_STYLE   CS_HREDRAW | CS_VREDRAW
#define DEF_WND_STYLE     WS_OVERLAPPEDWINDOW
#define DEF_ICON          IDI_WINLOGO
#define DEF_CURSOR        IDC_ARROW
#define DEF_BACKGROUND    (HBRUSH)GetStockObject(NULL_BRUSH)
#define DEF_TITLE         "Basic Window"
#define DEF_USE_PEEK      true
#define DEF_USE_WAIT      false
#define DEF_IS_FULLSCREEN false
#define DEF_INSTANCE      GetModuleHandle(NULL)
#define DEF_WND_SHOW      SW_HIDE

//$$$ FIND ME A HOME
#include <sstream>

template <class T>
inline std::string ToString (const T& t) {
  std::stringstream ss;
  ss << t;
  return ss.str();
}
//$$$

/*******************************************************************************
  Window Class
*******************************************************************************/
class Window {
  public:
    // Type Definitions
    typedef long(* MsgProc)(Window&, long, long);
    typedef std::map<long, MsgProc> MsgMap;
    typedef MsgMap::iterator MsgIter;

    typedef std::map<HWND, Window*> WndMap;
    typedef WndMap::iterator WndIter;

    typedef std::set<Window*> WndSet;
    typedef WndSet::iterator WSIter;

    // Creation and Destruction
    Window();
    ~Window();

    virtual bool Create();
    virtual void Destroy();

    // Window Control
    void ShowWnd();
    void ShowWnd( UINT style );
    void SetForegroundWnd();
    void SetFocus();
    
    void UpdateSettings();
    
    void NotifyWndClose();
    static void NotifyAppQuit();
    
    // Message and Procedure Handling andling
    bool HandleMsgs();
    MsgProc RegisterProc( UINT message, MsgProc func );
    MsgProc RegisterIfNoProc( UINT message, MsgProc func );
    MsgProc ClearProc( UINT message );
    void ClearAllProc();

    // Sets
    inline void UsePeek( bool state )         { usePeek = state; }
    inline void UseWait( bool state )         { useWait = state; }

    inline void SetHeight( UINT h )           { height = h; }
    inline void SetWidth( UINT w )            { width = w; }
    inline void SetSize( UINT h, UINT w )     { SetHeight(h); SetWidth(w); }
    inline void SetPosX( UINT x )             { posX = x; }
    inline void SetPosY( UINT y )             { posY = y; }
    inline void SetPos( UINT x, UINT y )      { SetPosX(x); SetPosY(y); }
    inline void SetClassStyle( UINT style )   { classStyle = style; }
    inline void SetWndStyle( DWORD style )    { wndStyle = style; }
    inline void SetWndIcon( LPCTSTR i )       { icon = i; }
    inline void SetWndCursor( LPCTSTR c )     { cursor = c; }
    inline void SetBackground( HBRUSH b )     { background = b; }
    inline void SetBackground( int b )        { background =
                                                (HBRUSH)GetStockObject(b); }
    inline void SetWndTitle( const char* t )  { titleName = t; }
    inline void SetInstance( HINSTANCE i )    { hInst = i; }
    inline void SetFullScreen( bool state )   { isFullScreen = state; }
    
    // Gets
    inline HWND       GetHwnd()     { return hWnd; }
    inline HINSTANCE  GetInstance() { return hInst; }
    inline UINT       GetHeight() { return height; }
    inline UINT       GetWidth() { return width; }
    inline UINT       GetPosX() { return posX; }
    inline UINT       GetPosY() { return posY; }

    inline static int CurrentWndCount() { return numWindows; }
    inline static int ActiveWndCount() { return (int)wndControl.size(); }

    // Checks
    inline bool IsActive()            { return isActive; }
    inline bool IsFullScreen()        { return isFullScreen; }
    inline static bool IsQuiting()    { return isQuiting; }
    inline static bool IsTerminated() { return isQuiting || !ActiveWndCount(); }
   
    // Defualt Procedures
    static long DefWndClose( Window& win, long wparam, long lparam );
    static long DefWndSize( Window& win, long wparam, long lparam );
    static long DefWndMove( Window& win, long wparam, long lparam );
    static long DefWndActivate( Window& win, long wparam, long lparam );
    
  protected:
    // Window Handling
    bool CreateWnd();
    void DestroyWnd();

    void FullScreenMode();
    void WindowMode();
    
    inline void AddWndCount()    { ++numWindows; }
    inline void RemoveWndCount() { --numWindows;  }

    // Sets
    inline static void SetQuit( bool state )   { isQuiting = state; }
    inline void SetActive( bool state ) { isActive = state; }

    // Gets
    inline UINT GetClassStyle() { return classStyle; }
    inline DWORD GetWndStyle() { return wndStyle; }
    inline LPCTSTR GetIcon() { return icon; }
    inline LPCTSTR GetCursor() { return cursor; }
    inline HBRUSH GetBackgroundColor() { return background; }
    inline std::string GetMenuName() { return menuName; }
    inline std::string GetTitleName() { return titleName; }
    
    // Window class generation
    void RegisterWndClass( WNDCLASS& wc );
    void UnregisterWndClass();

    RECT GenerateWndRect();
    WNDCLASS GenerateWndClass();
    void GenerateClassName();

  private:
    // Window Handling
    static void AddWndContact( HWND hwnd, Window* win );
    void RemoveWndContact();

    void AddWndPtr();
    void RemoveWndPtr();
    static void RemoveWndPtr( WSIter& iter );
    
    // Message Handling
    static LRESULT CALLBACK MessageRouter( HWND hwnd, UINT msg, WPARAM wparam,
                                           LPARAM lparam );
    MsgIter GetMsgProc( UINT message );
    MsgIter GetMsgEnd();

    // Sets
    inline void SetHwnd( HWND h ) { hWnd = h; }

    // Static Flags
    static bool isQuiting;
    static int numWindows;

    // Non-static Flags
    bool isActive;
    bool isFullScreen;
    bool usingFullScreen;
    bool usePeek;
    bool useWait;

    // Window Information
    UINT      currentShowWnd;
    HWND      hWnd;
    HINSTANCE hInst;

    // Class Information
    UINT      height, width;
    UINT      posX, posY;
    UINT      classStyle;
    DWORD     wndStyle;
    LPCTSTR   icon;
    LPCTSTR   cursor;
    HBRUSH    background;
    std::string menuName;
    std::string className;
    std::string titleName;

    // Message and Window Maps
    MsgMap msgControl;
    static WndMap wndControl;
    static WndSet wndRegistry;
};

#endif  // Window Class
